/*
 * Copyright (C) 2008 Alessandro Sivieri <alessandro.sivieri@chimera-bellerofonte.eu>.
 *
 *  This file is part of Daex.
 *
 *  Daex is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Foobar is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */
package eu.chimera_bellerofonte.tesi.ui.models;

import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

import eu.chimera_bellerofonte.tesi.DaexLogger;
import eu.chimera_bellerofonte.tesi.datatypes.Album;
import eu.chimera_bellerofonte.tesi.datatypes.Song;
import eu.chimera_bellerofonte.tesi.ui.DaexTableCellRenderer;


/**
 * <p>Classe di estensione della tabella standard di Swing; sovrascrive
 * il metodo di creazione dell'editor per le ComboBox; viene dichiarata
 * in questo modo per comodita' di utilizzo con il modello.</p>
 * <p>La strutturazione della tabella e' divisa in Java tra il modello
 * e la parte di effettiva interfaccia grafica; l'attuale implementazione
 * qui presentata si occupa di gestire le modifiche alla base di dati
 * (oggetto Album) quando tali modifiche vengono fatte tramite la tabella
 * stessa (ad esempio modificandone i dati tramite le celle editabili);
 * qualunque modifica effettuata invece al di fuori di questo sistema
 * deve essere notificata alla tabella tramite il metodo refresh(), che
 * esegue appunto un refresh completo delle celle.
 * <br>
 * L'implementatore del sistema puo' comunicare in fase di costruzione
 * la base di dati da cui lavorare (costruendo un oggetto Album a partire
 * da un dvd fisico o da un file salvato) oppure la puo' agganciare in
 * un secondo momento (e successivamente alla costruzione la tabella
 * mostrera' quindi solamente le colonne senza alcuna riga); in questo
 * secondo caso il refresh viene eseguito automaticamente.</p>
 * 
 * @author Sivieri Alessandro
 * @author Vitucci Nicola
 * @version 16/mag/07
 */
public class DaexTable extends JTable
{
	static final long serialVersionUID = 1L;

	/**
	 * Numero fuori dal range delle colonne, che permette di
	 * ottenere l'istanza completa dell'oggetto visualizzato
	 * (anziche' la sola parte di rendering di una determinata
	 * cella).
	 */
	public static final int OBJECTCOLUMNINDEX=42; // omaggio a Douglas Adams
	
	private Album album=null;
	private AbstractTableModel model=null;
	private boolean invalidate=false;
	
	/**
	 * Metodo per l'inizializzazione della tabella, abilitando il drag
	 * and drop, le modalita' di selezione ed il modello dei dati.
	 */
	private void init()
	{
		this.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		this.setTransferHandler(new DaexSongTransferHandler());
		this.setDragEnabled(true);
		this.model=new AbstractTableModel()
		{
			
			static final long serialVersionUID = 1L;
			private String[] columns={"Traccia fisica", "Artista", "Titolo", "Canzone n."};

			/**
			 * @see javax.swing.table.TableModel#getColumnCount()
			 */
			public int getColumnCount()
			{
				return columns.length;
			}

			/**
			 * @see javax.swing.table.TableModel#getRowCount()
			 */
			public int getRowCount()
			{
				return ( album != null && !invalidate ) ? album.getSongs().size() : 0;
			}

			/**
			 * Metodo per ottenere le varie parti visualizzate nelle colonne;
			 * implementa per la variabile OBJECTCOLUMNINDEX la possibilita'
			 * di ritornare il completo oggetto relativo alla riga.
			 * 
			 * @see eu.chimera_bellerofonte.tesi.ui.DaexTracksTableModel#OBJECTCOLUMNINDEX
			 * @see javax.swing.table.TableModel#getValueAt(int, int)
			 */
			public Object getValueAt(int arg0, int arg1)
			{
				Object o=null;
				switch(arg1)
				{
					case 0:
						o=arg0+1;
						break;
					case 1:
						o=album.getSongs().get(arg0).getArtist();
						break;
					case 2:
						o=album.getSongs().get(arg0).getTitle();
						break;
					case 3:
						o=album.getSongs().get(arg0).getTableValue();
						break;
					case OBJECTCOLUMNINDEX:
						o=album.getSongs().get(arg0);
						break;
					default:
						DaexLogger.getDaexLogger().severe("Si cerca di raggiungere un valore in una colonna errata: "+arg1);
						break;
				}
				return o;
			}

			/**
			 * @see javax.swing.table.AbstractTableModel#getColumnName(int)
			 */
			@Override
			public String getColumnName(int arg0)
			{
				return columns[arg0];
			}

			/**
			 * @see javax.swing.table.AbstractTableModel#isCellEditable(int, int)
			 * @todo aggiungere un flag a Song per indicare un autore diverso da Album?
			 */
			@Override
			public boolean isCellEditable(int arg0, int arg1)
			{
				if(arg0==0 && arg1==3)
				{
					return false;
				}
				return ( arg1 != 0 ) ? true : false;
			}

			/**
			 * @see javax.swing.table.AbstractTableModel#setValueAt(java.lang.Object, int, int)
			 */
			@Override
			public void setValueAt(Object arg0, int arg1, int arg2)
			{
				switch(arg2)
				{
					case 1:
						album.getSongs().get(arg1).setArtist((String) arg0);
						break;
					case 2:
						String old=album.getSongs().get(arg1).getTitle();
						for(Song s: album.getSongs())
						{
							if(s.getTitle().equals(old))
							{
								s.setTitle((String) arg0);
							}
						}
						break;
					case 3:
						// aggiornamento della traccia attuale e, se non siamo
						// nell'ultima riga, di tutte le tracce che seguono
						int prec=album.getSongs().get(arg1).getTableValue();
						int act=(Integer) arg0;
						int diff=prec-act;
						album.getSongs().get(arg1).setTableValue((Integer) arg0);
						if(album.getSongs().get(arg1).getTableValue()==album.getSongs().get(arg1-1).getTableValue())
						{
							album.getSongs().get(arg1).setTitle(album.getSongs().get(arg1-1).getTitle());
						}
						else
						{
							album.getSongs().get(arg1).setTitle(Album.GENERICTITLE+album.getSongs().get(arg1).getAbsoluteValue());
						}
						if(arg1!=album.getSongs().size()-1)
						{
							for(Song s: album.getSongs().subList(arg1+1, album.getSongs().size()))
								s.setTableValue(s.getTableValue()-diff);
						}
						break;
					default:
						DaexLogger.getDaexLogger().severe("Si sta editando la colonna "+arg2+", inesistente!");
						break;
				}
				if(arg2==1)
				{
					fireTableCellUpdated(arg1, arg2);
				}
				else
				{
					fireTableDataChanged();
				}
			}

			/**
			 * @see javax.swing.table.AbstractTableModel#getColumnClass(int)
			 */
			@Override
			public Class<?> getColumnClass(int arg0)
			{
				return getValueAt(0, arg0).getClass();
			}
		};
		this.setModel(model);
	}

	/**
	 * Costruttore; da utilizzare se non si ha un oggetto
	 * album pronto.
	 */
	public DaexTable()
	{
		super();
		init();
	}
	
	/**
	 * Costruttore; setta gli attributi di classe con i
	 * valori specificati.
	 * 
	 */
	public DaexTable(Album album)
	{
		super();
		this.album=album;
		init();
	}
	
	/**
	 * Metodo utilizzato per settare un nuovo album nella tabella
	 * delle tracce; il refresh della tabella viene eseguito in
	 * maniera automatica.
	 * 
	 * @param album il nuovo album da settare
	 */
	public void setAlbum(Album album)
	{
		this.album=album;
		this.model.fireTableDataChanged();
	}
	
	/**
	 * Questo metodo ritorna il valore attuale di album.
	 * 
	 * @return il valore attuale di album
	 */
	public Album getAlbum()
	{
		return album;
	}

	/**
	 * Metodo per il refresh completo.
	 */
	public void refresh()
	{
		this.model.fireTableDataChanged();
	}
	
	/**
	 * Questo metodo ritorna il valore attuale di invalidate.
	 * 
	 * @return il valore attuale di invalidate
	 */
	public boolean isInvalidate()
	{
		return invalidate;
	}

	/**
	 * Questo metodo setta invalidate con il valore dato.
	 * 
	 * @param invalidate il nuovo valore per questo campo
	 */
	public void setInvalidate(boolean invalidate)
	{
		this.invalidate = invalidate;
		refresh();
	}

	/**
	 * @see javax.swing.JTable#getCellRenderer(int, int)
	 */
	@Override
	public TableCellRenderer getCellRenderer(int row, int column)
	{
		if(column!=0)
		{
			return super.getCellRenderer(row, column);
		}
		else
		{
			return new DaexTableCellRenderer();
		}
	}

	/**
	 * @see javax.swing.JTable#getCellEditor(int, int)
	 */
	@Override
	public TableCellEditor getCellEditor(int row, int column)
	{
		if(column==3 && row!=0)
		{
			JComboBox box=new JComboBox();
			int prec=(Integer) getValueAt(row-1, column);
			int act=(Integer) getValueAt(row, column);
			if(prec==act)
			{
				box.addItem(act);
				box.addItem(act+1);
			}
			else
			{
				box.addItem(prec);
				box.addItem(act);
			}
			return new DefaultCellEditor(box);
		}
		else
		{
			return super.getCellEditor(row, column);
		}
	}

}
